﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using BloxGin.Cameras;
using BloxGin.Blocks;


namespace BloxGame.Player
{
    public class Player
    {
        private Game1 _game;
        private World _world;
        private Vector3 _position;
        private Vector3 _playerVelocity;
        private double _headBob;

        MouseState currentMouseState;
        MouseState previousMouseState;

        BloxGin._3DMouse mouseCursor;
        Matrix mouseCursorWorld;

        public Player(Game1 game,World world, Vector3 startPosition)
        {
            _game = game;
            _world = world;
            _position = startPosition;
            mouseCursor = new BloxGin._3DMouse(game.GraphicsDevice);
        }

        public Vector3 Position
        {
            get { return _position; }
        }

        public ICamera Camera
        {
            get
            {
                return _game.Camera;
            }
        }

        public void Initialize()
        {
            _playerVelocity = Vector3.Zero;

        }

        public void LoadContent()
        {

        }

        Matrix _rotationMatrix;
        Vector3 _lookVector;
        Random r = new Random();

        public Vector3 LookVector
        {
            get { return _lookVector; }
        }

        public void Update(GameTime gameTime)
        {
            _game.Window.Title = mouseCursorWorld.Translation.ToString();
            currentMouseState = Mouse.GetState();
            _rotationMatrix = Camera.cameraRotation;
            _lookVector = Vector3.Transform(Vector3.Forward, _rotationMatrix);
            _lookVector.Normalize();



           
            if (_game.input.IsNewKeyPress(Keys.Space))
            {
                Vector3 footPosition = _position + new Vector3(0f, -1.5f, 0f);

                if (_world.SolidAtPoint(footPosition) && _playerVelocity.Y == 0)
                {
                    _playerVelocity.Y = 5;
                    float amountBelowSurface = ((ushort)footPosition.Y) + 1 - footPosition.Y;
                    _position.Y += amountBelowSurface + 0.01f;
                }

                 
            }
            if (_game.input.IsNewKeyPress(Keys.T))
                if (Camera is FirstPersonCamera)
                    ((FirstPersonCamera)(Camera)).Shake(0.1f, 2);
            CheckBuild(gameTime);
            UpdatePosition(gameTime);

            float headbobOffset = (float)Math.Sin(_headBob) * 0.06f;

            if (Camera is FirstPersonCamera)
                Camera.Position = _position + new Vector3(0, 0.15f + headbobOffset, 0);

           

            previousMouseState = currentMouseState;
        }

        public void CheckBuild(GameTime gameTime)
        {
                     
         
            //if (_game.input.LeftMouseClick())
            {
                mouseCursorWorld = Matrix.Identity;
                float hit = 0;
                for (float x = 0.8f; x < 10f; x += 0.1f)
                {
                    Vector3 targetPoint = Camera.Position + (_lookVector * x);
                    if (_world.BlockAtPoint(targetPoint) != BlockTypes.None)
                    {
                        hit = x;
                        int xi = (int)targetPoint.X;
                        int yi = (int)targetPoint.Y;
                        int zi = (int)targetPoint.Z+1;
                        mouseCursorWorld = Matrix.CreateTranslation(xi, yi, zi);
                        break;
                    }
                }
                if (hit != 0 && _game.input.LeftMouseClick())
                {
                    for (float x = hit; x > 0.7f; x -= 0.1f)
                    {
                        Vector3 targetPoint = Camera.Position + (_lookVector * x);
                        if (_world.BlockAtPoint(targetPoint) == BlockTypes.None)
                        {
                            _world.AddSolid((ushort)targetPoint.X, (ushort)targetPoint.Y, (ushort)targetPoint.Z, BlockTypes.Dirt);
                            
                            break;
                        }
                    }
                }
            }
        }



        private void UpdatePosition(GameTime gameTime)
        {
            _playerVelocity.Y +=-10 * (float)gameTime.ElapsedGameTime.TotalSeconds;
            Vector3 footPosition = _position + new Vector3(0f, -1.5f, 0f);
            Vector3 headPosition = _position + new Vector3(0f, 0.1f, 0f);

            if (_world.SolidAtPoint(footPosition) || _world.SolidAtPoint(headPosition))
            {
                BlockTypes standingOnBlock = _world.BlockAtPoint(footPosition);
                BlockTypes hittingHeadOnBlock = _world.BlockAtPoint(headPosition);

        
                // If the player has their head stuck in a block, push them down.
                if (_world.SolidAtPoint(headPosition))
                {
                    int blockIn = (int)(headPosition.Y);
                    _position.Y = (float)(blockIn - 0.15f);
                }

                // If the player is stuck in the ground, bring them out.
                // This happens because we're standing on a block at -1.5, but stuck in it at -1.4, so -1.45 is the sweet spot.
                if (_world.SolidAtPoint(footPosition))
                {
                    int blockOn = (int)(footPosition.Y);
                    _position.Y = (float)(blockOn + 1 + 1.45);
                }

                _playerVelocity.Y = 0;

            
            }

 
            _position += _playerVelocity * (float)gameTime.ElapsedGameTime.TotalSeconds;

            Vector3 moveVector = new Vector3(
               
                GamePad.GetState(PlayerIndex.One).ThumbSticks.Left.X,
                0,
                -GamePad.GetState(PlayerIndex.One).ThumbSticks.Left.Y);

            PlayerIndex controlIndex;
            
            if (_game.input.IsKeyDown(Keys.W))
            {
                moveVector += Vector3.Forward * 2;
            }
            if (_game.input.IsKeyDown(Keys.S))
            {
                moveVector += Vector3.Backward * 2;
            }
            if (_game.input.IsKeyDown(Keys.A))
            {
                moveVector += Vector3.Left * 2;
            }
            if (_game.input.IsKeyDown(Keys.D))
            {
                moveVector += Vector3.Right * 2;
            }

            //moveVector.Normalize();
            moveVector *= 3.5f * (float)gameTime.ElapsedGameTime.TotalSeconds;

            Vector3 rotatedMoveVector = Vector3.Transform(moveVector, Camera.cameraRotation);

            // Attempt to move, doing collision stuff.
            if (TryToMoveTo(rotatedMoveVector, gameTime)) { }
            else if (!TryToMoveTo(new Vector3(0, 0, rotatedMoveVector.Z), gameTime))
            {
            }
            else if (!TryToMoveTo(new Vector3(rotatedMoveVector.X, 0, 0), gameTime)) 
            {
            }
        }

        private bool TryToMoveTo(Vector3 moveVector, GameTime gameTime)
        {
            // Build a "test vector" that is a little longer than the move vector.
            float moveLength = moveVector.Length();
            Vector3 testVector = moveVector;
            testVector.Normalize();
            testVector = testVector * (moveLength + 0.3f);

            // Apply this test vector.
            Vector3 movePosition = _position + testVector;
            Vector3 midBodyPoint = movePosition + new Vector3(0, -0.7f, 0);
            Vector3 lowerBodyPoint = movePosition + new Vector3(0, -1.4f, 0);

            if (!_world.SolidAtPoint(movePosition) && !_world.SolidAtPoint(lowerBodyPoint) && !_world.SolidAtPoint(midBodyPoint))
            {
                _position = _position + moveVector;
                if (moveVector != Vector3.Zero)
                {
                    _headBob += 0.1;
                }
                return true;
            }

        
            BlockTypes lowerBlock = _world.BlockAtPoint(lowerBodyPoint);
            BlockTypes midBlock = _world.BlockAtPoint(midBodyPoint);
            BlockTypes upperBlock = _world.BlockAtPoint(movePosition);

            return false;
        }

        public void Draw(GameTime gameTime)
        {
            mouseCursor.Draw(Camera.View, Camera.Projection, mouseCursorWorld);
        }
    }
}
